package com.sheepone.util.console.table;

import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * Create by IntelliJ IDEA
 * console table
 * @author chenlei
 * @dateTime 2018/12/11 9:28
 * @description ConsoleTable
 */
public class ConsoleTableUtil {
    private ConsoleTableUtil() {
    }

    public static ConsoleTable newTable(){
        return new ConsoleTable();
    }
    
    public static class ConsoleTable {
        private final Header header = new Header();
        private final Body body  = new Body();
        String lineSep = "\n";
        String verticalSep = "|";
        String horizontalSep = "-";
        String joinSep = "+";
        int[] columnWidths;
        NullCellPolicy nullPolicy = NullCellPolicy.EMPTY_STRING;
        boolean restrict = false;

        List<String> getLines(){
            List<String> lines = new ArrayList<>();
            if((header != null && !header.isEmpty()) || (body != null && !body.isEmpty())){
                lines.addAll(header.print(columnWidths,horizontalSep,verticalSep,joinSep));
                lines.addAll(body.print(columnWidths,horizontalSep,verticalSep,joinSep));
            }
            return lines;
        }

        @Override
        public String toString() {
            return StringUtils.join(getLines(), lineSep);
        }

        private ConsoleTable(){
        }

        public ConsoleTable addHead(Cell cell){
            this.header.addHead(cell);
            return this;
        }

        public ConsoleTable addRow(List<Cell> row){
            this.body.addRow(row);
            return this;
        }

        public ConsoleTable addHeaders(List<Cell> headers){
            this.header.addHeads(headers);
            return this;
        }

        public ConsoleTable addRows(List<List<Cell>> rows){
            this.body.addRows(rows);
            return this;
        }

        public ConsoleTable addHeaders(Cell... headers){
            this.header.addHeads(List.of(headers));
            return this;
        }

        public ConsoleTable addRows(List<Cell>... rows){
            this.body.addRows(List.of(rows));
            return this;
        }

        public ConsoleTable lineSep(String lineSep){
            this.lineSep = lineSep;
            return this;
        }

        public ConsoleTable verticalSep(String verticalSep){
            this.verticalSep = verticalSep;
            return this;
        }

        public ConsoleTable horizontalSep(String horizontalSep){
            this.horizontalSep = horizontalSep;
            return this;
        }

        public ConsoleTable joinSep(String joinSep){
            this.joinSep = joinSep;
            return this;
        }

        public ConsoleTable nullPolicy(NullCellPolicy nullPolicy){
            this.nullPolicy = nullPolicy;
            return this;
        }

        public ConsoleTable restrict(){
            this.restrict = true;
            return this;
        }

        public void print(){
            //compute max column widths
            if(!this.header.isEmpty() || !this.body.isEmpty()){
                List<List<Cell>> allRows = new ArrayList<>();
                allRows.add(this.header.cells);
                allRows.addAll(this.body.rows);
                int maxColumn = allRows.stream().map(List::size).mapToInt(size -> size).max().getAsInt();
                int minColumn = allRows.stream().map(List::size).mapToInt(size -> size).min().getAsInt();
                if(maxColumn != minColumn && this.restrict){
                    throw new IllegalArgumentException("number of columns for each row must be the same when strict mode used.");
                }
                this.columnWidths = new int[maxColumn];
                for (List<Cell> row : allRows) {
                    for (int i = 0; i < row.size(); i++) {
                        Cell cell = row.get(i);
                        if(cell == null || cell.getValue() == null){
                            cell = this.nullPolicy.getCell(cell);
                            row.set(i,cell);
                        }
                        int length = StringPadUtil.strLength(cell.getValue().toString());
                        if(this.columnWidths[i] < length){
                            this.columnWidths[i] = length;
                        }
                    }
                }
            }
            System.out.println(this);
        }
    }

}